﻿using System;
using System.Collections.Generic;
using System.Web;
using FBGraph.Web.Security;

namespace FBGraph.Web
{
    /// <summary>Encapsulates web-application-specific Graph API client functionality.</summary>
    public class FBGraphHttpContext : FBGraphContext, IFBGraphHttpContext
    {
        internal const String HTTP_CONTEXT_ITEMS_KEY = "FBGraph_FBGraphHttpContext";
        internal const String ACCESS_TOKEN_COOKIE_NAME = "AccessToken";

        /// <summary>Initializes an instance of <see cref="FBGraphHttpContext" />.</summary>
        protected FBGraphHttpContext()
        {
            this.Cookies = new CookieManager(this);
        }

        private static FBGraphHttpContext Instantiate(HttpContextBase httpContext)
        {
            var context = new FBGraphHttpContext();
            context.HttpContext = httpContext;
            return context;
        }

        /// <summary>Gets a reference to the current <typeparamref name="FBGraphContext" /> instance for the HTTP request.</summary>
        /// <param name="httpContext">A reference to the <see cref="HttpContextBase" /> for the current HTTP request.</param>
        /// <returns>A reference to the current <typeparamref name="FBGraphContext" /> instance for the HTTP request.</returns>
        public static FBGraphHttpContext Current(HttpContextBase httpContext)
        {
            var context = (FBGraphHttpContext)httpContext.Items[HTTP_CONTEXT_ITEMS_KEY];
            if(context == null)
            {
                context = Instantiate(httpContext);
                httpContext.Items[HTTP_CONTEXT_ITEMS_KEY] = context;
            }
            return context;
        }

        /// <summary>Gets a reference to the <see cref="HttpContextBase" /> for the <typeparamref name="FBGraphContext" />.</summary>
        protected internal HttpContextBase HttpContext { get; private set; }
        HttpContextBase IFBGraphHttpContext.HttpContext { get { return this.HttpContext; } }

        /// <summary>Provides access to user- and application-specific cookies.</summary>
        public CookieManager Cookies { get; private set; }

        /// <summary>Removes the Graph API access token for the current user.</summary>
        public void RemoveAccessToken()
        {
            SetAccessToken(null, DateTime.MaxValue);
        }

        /// <summary>Sets the Graph API access token for the current user.</summary>
        /// <param name="token">The <see cref="String" /> access token.</param>
        /// <param name="expires">The expiration date/time of the access token.</param>
        public void SetAccessToken(String token, DateTime expires)
        {
            if (String.IsNullOrEmpty(token))
            {
                if (Cookies.Contains(ACCESS_TOKEN_COOKIE_NAME))
                {
                    Cookies.Remove(ACCESS_TOKEN_COOKIE_NAME);
                    accessTokenChecked = true;
                    _accessToken = null;
                }
            }
            else
            {
                accessTokenChecked = true;
                _accessToken = new AccessToken(token, expires);

                Cookies.Set(ACCESS_TOKEN_COOKIE_NAME, _accessToken.Value, expires);
            }
        }

        private Boolean accessTokenChecked;
        private AccessToken? _accessToken;
        /// <summary>Gets the access token stored for the current user.</summary>
        public override AccessToken? AccessToken
        {
            get
            {
                if (!_accessToken.HasValue && !accessTokenChecked)
                {
                    if (Cookies.Contains(ACCESS_TOKEN_COOKIE_NAME)) _accessToken = Cookies.Get<AccessToken>(ACCESS_TOKEN_COOKIE_NAME);
                    else _accessToken = null;

                    accessTokenChecked = true;
                }
                return _accessToken;
            }
        }

        private Boolean extendedPermissionsChecked;
        private ExtendedPermission _extendedPermissions;
        /// <summary>Gets a value representing the known <see cref="ExtendedPermission" />s granted to the application by the current user.</summary>
        public override ExtendedPermission ExtendedPermissions
        {
            get
            {
                if (!extendedPermissionsChecked)
                {
                    _extendedPermissions = this.GetExtendedPermissions();
                    extendedPermissionsChecked = true;
                }
                return _extendedPermissions;
            }
            protected set { }
        }

        /// <summary>Implements <see cref="FBGraphContext.BeforeGraphApiRequest" /> to automatically refresh the user's access token before making a Graph API request.</summary>
        protected override void BeforeGraphApiRequest()
        {
            this.RefreshAccessToken();
        }
    }
}